package org.litespring.aop.aspectj;

import org.litespring.aop.Advice;
import org.litespring.aop.MethodMatcher;
import org.litespring.aop.Pointcut;
import org.litespring.aop.framework.AopConfigSupport;
import org.litespring.aop.framework.AopProxyFactory;
import org.litespring.aop.framework.CglibProxyFactory;
import org.litespring.aop.framework.JdkAopProxyFactory;
import org.litespring.beans.BeansException;
import org.litespring.beans.factory.config.BeanPostProcessor;
import org.litespring.beans.factory.config.ConfigurableBeanFactory;
import org.litespring.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author ChangLiang
 * @date 2020/9/5
 */
public class AspectJAutoProxyCreator implements BeanPostProcessor {

    ConfigurableBeanFactory beanFactory;

    public void setBeanFactory(ConfigurableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @Override
    public Object beforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object afterInitialization(Object bean, String beanName) throws BeansException {
        // 如果这个Bean本身就是Advice及其子类，那就不要再生成动态代理了
        if (isInfrastructureClass(bean.getClass())) {
            return bean;
        }
        List<Advice> advices = getCandidateAdvices(bean);
        if (advices.isEmpty()) {
            return bean;
        }
        return createProxy(advices, bean);
    }

    protected Object createProxy(List<Advice> advices, Object bean) {
        AopConfigSupport config = new AopConfigSupport();
        advices.forEach(advice -> config.addAdvice(advice));
        Set<Class> targetInterfaces = ClassUtils.getAllInterfacesForClassAsSet(bean.getClass());
        targetInterfaces.forEach(targetInterface -> config.addInterface(targetInterface));
        config.setTargetObject(bean);

        AopProxyFactory proxyFactory = null;
        // 沒有实现接口 于是使用cglib
        if (config.getProxiedInterfaces().length == 0) {
            proxyFactory = new CglibProxyFactory(config);
        } else {
            proxyFactory = new JdkAopProxyFactory(config);
        }
        return proxyFactory.getProxy();
    }

    protected boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass);
    }

    private List<Advice> getCandidateAdvices(Object bean) {
        List<Object> adviceObjects = this.beanFactory.getBeansByType(Advice.class);
        List<Advice> advices = adviceObjects.stream().map(adviceObj -> (Advice) adviceObj).collect(Collectors.toList());
        return advices.stream().filter(advice -> canApply(advice.getPointcut(), bean.getClass())).collect(Collectors.toList());
    }

    /**
     * 判断是否需要将Pointcut weave into targetClass
     *
     * @param pc
     * @param targetClass
     * @return
     */
    public static boolean canApply(Pointcut pc, Class<?> targetClass) {
        MethodMatcher methodMatcher = pc.getMethodMatcher();

        // 获取目标targetClass的所有实现了的接口（包括其父类实现了的）
        LinkedHashSet<Class> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class clazz : classes) {
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (methodMatcher.matches(method)) {
                    return true;
                }
            }
        }
        return false;
    }
}
